package model;

import java.awt.Toolkit;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;

public class Database implements Serializable {
	private static final long serialVersionUID = 1529925802879968933L;
	private volatile static Database instance;
	public static String PATH = "./data/Database.sm";
	public static String RESOURCE_PATH = "/data/Database.sm";
	private String userName;
	private String password;
	private String host;
	private String port;
	private String dbName;
	private String url;

	private Database() throws ClassNotFoundException {
		Class.forName("com.mysql.jdbc.Driver");
		this.userName = "root";
		this.password = "123456789";
		this.host = "localhost";
		this.port = "3306";
		this.dbName = "storemanager";
		this.url = new StringBuilder("jdbc:mysql://").append(host).append(":")
				.append(port).append("/").append(dbName).toString();
	}

	public static Database getInstance() throws ClassNotFoundException {
		if (instance == null) {
			synchronized (Database.class) {
				if (instance == null) {
					return newInstance();
				}
			}
		}
		return instance;
	}

	public static Database newInstance() throws ClassNotFoundException {
		try {
			instance = readDatabaseObj(); // read object from file
		} catch (IOException | NullPointerException e) {
			instance = new Database(); // create new instance of database object
		}
		return instance;
	}

	public Connection getConnection() throws SQLException {
		return DriverManager.getConnection(url, userName, password);
	}

	public static Connection connect() throws ClassNotFoundException,
			SQLException {
		Database database = Database.getInstance();
		return database.getConnection();
	}

	private void resetURL() {
		this.url = new StringBuilder("jdbc:mysql://").append(host).append(":")
				.append(port).append("/").append(dbName).toString();
	}

	private static Database readDatabaseObj() throws ClassNotFoundException,
			IOException {
		Database db = null;
		ObjectInputStream ois = null;

		try { // folder
			ois = new ObjectInputStream(new FileInputStream(PATH));
		} catch (FileNotFoundException e) { // source
			ois = new ObjectInputStream(Toolkit.getDefaultToolkit().getClass()
					.getResourceAsStream(RESOURCE_PATH));
			restoreDatabase();
			IOFile.createFontFile();
		}

		db = (Database) ois.readObject();
		ois.close();
		return db;
	}

	public void saveDatabaseObj() throws FileNotFoundException, IOException {
		ObjectOutputStream oos = null;
		try {
			File file = new File(PATH);
			IOFile.createParentFile(PATH);
			oos = new ObjectOutputStream(new FileOutputStream(file));
			oos.writeObject(instance);
			oos.flush();
		} finally {
			if (oos != null)
				try {
					oos.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
		}
	}

	public static void restoreDatabase() {
		try {
			BufferedInputStream bis = new BufferedInputStream(Toolkit
					.getDefaultToolkit().getClass()
					.getResourceAsStream(RESOURCE_PATH));
			BufferedOutputStream bos = new BufferedOutputStream(
					new FileOutputStream(PATH));

			IOFile.createParentFile(PATH);
			IOFile.copyFile(bis, bos);

			bis.close();
			bos.close();
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	public String getUserName() {
		return userName;
	}

	public void setUserName(String userName) {
		this.userName = userName;
	}

	public String getPassword() {
		return password;
	}

	public void setPassword(String password) {
		this.password = password;
	}

	public String getHost() {
		return host;
	}

	public void setHost(String host) {
		this.host = host;
		resetURL();
	}

	public String getPort() {
		return port;
	}

	public void setPort(String port) {
		this.port = port;
		resetURL();
	}

	public String getDbName() {
		return dbName;
	}

	public void setDbName(String dbName) {
		this.dbName = dbName;
		resetURL();
	}

	@Override
	public String toString() {
		return "Database [userName=" + userName + ", password=" + password
				+ ", host=" + host + ", port=" + port + ", dbName=" + dbName
				+ ", url=" + url + "]";
	}

}
